package connectors;

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

import tests.XORTest;
import utils.SynapseRandomizer;
import utils.math_utils.MathUtils;
import utils.math_utils.RandStream;

import components.AbstractNeuron;
import components.LIFNeuron;
import components.Synapse;

public class GridConnect extends Connect {
	
	private static final float DEFAULT_EE = 0.3f;
	private static final float DEFAULT_EI = 0.2f;
	private static final float DEFAULT_IE = 0.4f;
	private static final float DEFAULT_II = 0.1f;
	public static float maxDist;
	
	
	private float eeC = DEFAULT_EE;
	private float eiC = DEFAULT_EI;
	private float ieC = DEFAULT_IE;
	private float iiC = DEFAULT_II;
	
	public GridConnect(SynapseRandomizer eeSR, SynapseRandomizer eiSR,
			SynapseRandomizer ieSR, SynapseRandomizer iiSR) {
		super(eeSR, eiSR, ieSR, iiSR);
	}
	
	public GridConnect() {
		
	}
	
	public Synapse [][] stochasticConnect(AbstractNeuron[] source,
			AbstractNeuron [] target, double lambda) {
		weightMatrix  = new Synapse[source.length][target.length];
		int i = 0;
		int j = 0;
		for (AbstractNeuron src : source) {
			j = 0;
			for (AbstractNeuron targ : target) {
				if(src != targ) {
					double cProb = genConnectProb(src, targ, lambda);
					if(Math.random() <= cProb) {
						Synapse s = new Synapse(src, targ);
						double dist = MathUtils.euclideanDist(src.getCoordinates(),
								targ.getCoordinates()) * 60;
						
						s.setDelay(dist/1000 + 0.8);
						
						if(src.isExcitatory() && targ.isExcitatory()) {
							getEeSR().randomizeSynapse(s);
							if(source == target)
								s.setStrength(3*RandStream.nextGaussian(30, 15, 1, 100, true));				
							else
								s.setStrength(RandStream.nextGaussian(18, 9, 1, 54, true));				
							s.setDecay(3);
							//s.setDelay(1.5);
						} else if (src.isExcitatory() && !targ.isExcitatory()) {
							getEiSR().randomizeSynapse(s);
							if(source == target)
								s.setStrength(3*RandStream.nextGaussian(60, 30, 1, 200, true));
							else
								s.setStrength(RandStream.nextGaussian(9, 4.5, 0.5, 36, true));				
							s.setDecay(3);
							//s.setDelay(0.8);
						} else if (!src.isExcitatory() && targ.isExcitatory()) {
							getIeSR().randomizeSynapse(s);
							s.setStrength(3*RandStream.nextGaussian(19, 8, 1, 38, true));
							s.setDecay(6);
							//s.setDelay(0.8);
						} else {			
							getIiSR().randomizeSynapse(s);
							s.setStrength(3*RandStream.nextGaussian(19, 8, 1, 38, true));
							s.setDecay(6);
							//s.setDelay(0.8);
						}
						//double dist = MathUtils.euclideanDist(src.getCoordinates(), targ.getCoordinates());
						//s.setDelay(0.8 + Math.log(dist)/2);
						src.getFanOut().add(s);
						targ.getFanIn().add(s);
						weightMatrix[i][j] = s;
					}
				}
				j++;
			}
			i++;
		}
		return weightMatrix;	
	}

	private double genConnectProb(AbstractNeuron src,
			AbstractNeuron targ, double lambda) {
		boolean ex = src.isExcitatory();
		boolean in = !targ.isExcitatory();
		float C = 0.0f;
		if(ex) {
			if(in) {
				C = eiC;
			} else {
				C = eeC;
			}
		} else {
			if(in) {
				C = ieC;
			} else {
				C = iiC;
			}
		}
		double dist = MathUtils.euclideanDist(src.getCoordinates(),
				targ.getCoordinates());
		return C * Math.exp(-Math.pow(dist, 2)/(lambda * lambda));		
	}
	
	
	/*/*******************************************************************
	 * 						GETTERS AND SETTERS							 *
	 *********************************************************************/
	
	public float getEeC() {
		return eeC;
	}

	public void setEeC(float eeC) {
		this.eeC = eeC;
	}

	public static float getMaxDist() {
		return maxDist;
	}

	public static void setMaxDist(float maxDist) {
		GridConnect.maxDist = maxDist;
	}

	public Synapse[][] getWeightMatrix() {
		return weightMatrix;
	}

	public void setWeightMatrix(Synapse[][] weightMatrix) {
		this.weightMatrix = weightMatrix;
	}

	public float getEiC() {
		return eiC;
	}

	public void setEiC(float eiC) {
		this.eiC = eiC;
	}

	public float getIeC() {
		return ieC;
	}

	public void setIeC(float ieC) {
		this.ieC = ieC;
	}

	public float getIiC() {
		return iiC;
	}

	public void setIiC(float iiC) {
		this.iiC = iiC;
	}
	
	public static void main (String [] args) {
		LIFNeuron [] neurons = new LIFNeuron[1000];
		int x = 0;
		int y = 0;
		int z = 0;
		for (int i = 0; i < neurons.length; i++) {
			neurons [i] = new LIFNeuron(Math.random() < 0.2, i);
			if (x > 9) {
				x = 0;
				y++;
			}
			if (y > 9) {
				y = 0;
				z++;
			}
			neurons[i].setCoordinates(x, y, z);
			x++;
		}
		GridConnect gc = new GridConnect();
		PreferrentialAttachment PA = new PreferrentialAttachment();
		XORTest.initializeSynVars(PA);
		XORTest.initializeSynVars(gc);
		Synapse [][] syns = gc.stochasticConnect(neurons, neurons, 2.25);
		syns = PA.addPreferentialConnections(neurons);
		try (FileWriter FW = new FileWriter("DegreesPA");
				PrintWriter pw = new PrintWriter(FW);) {
			for (LIFNeuron n : neurons) {
				pw.print(n.getFanOut().size() + ", ");
			}
			pw.println();
			for (LIFNeuron n : neurons) {
				pw.print(n.getFanIn().size() + ", ");
			}
		} catch (IOException ie) {
			ie.printStackTrace();
			System.exit(1);
		}
	}
	
}